import fs from "fs";
import JSZip from "jszip";
import path from "path";
import { DOMParser, XMLSerializer } from "xmldom";


// 判断是否汉语拼音（包含特殊 ɡ）
function isPinyin(text) {
  return /^[a-zA-ZɡāáǎàēéěèīíǐìōóǒòūúǔùǖǘǚǜüĀÁǍÀĒÉĚÈĪÍǏÌŌÓǑÒŪÚǓÙǕǗǙǛÜ\s]+$/.test(
    text.trim()
  );
}


// 复制 run 的样式
function cloneRunStyle(srcRun, doc) {
  const newRPr = doc.createElement("w:rPr");
  const oldRPr = srcRun.getElementsByTagName("w:rPr")[0];

  if (oldRPr) {
    for (let i = 0; i < oldRPr.childNodes.length; i++) {
      const child = oldRPr.childNodes[i];
      newRPr.appendChild(child.cloneNode(true));
    }
  }
  return newRPr;
}


// 找到段落中真正加粗的 run
function findBoldRun(p) {
  if (!p) return null;
  const raw = p.getElementsByTagName("w:r");
  if (!raw || typeof raw.length !== "number") return null;
  for (let i = 0; i < raw.length; i++) {
    const r = raw[i];
    const b = r.getElementsByTagName("w:b");
    if (b && b.length > 0) return r;
  }
  return null;
}


// 根据 <w:br/> 重新拆段落
function splitParagraphAtLineBreak(p, doc) {
  const newParagraphs = [];
  let currentP = doc.createElement("w:p");
  newParagraphs.push(currentP);

  function handleRunNode(runNode) {
    let currentRun = doc.createElement("w:r");
    let hasContentInCurrentRun = false;

    for (let j = 0; j < runNode.childNodes.length; j++) {
      const rc = runNode.childNodes[j];
      if (!rc) continue;

      if (rc.nodeName === "w:br") {
        if (hasContentInCurrentRun) currentP.appendChild(currentRun);

        currentP = doc.createElement("w:p");
        newParagraphs.push(currentP);

        currentRun = doc.createElement("w:r");
        hasContentInCurrentRun = false;
      } else {
        currentRun.appendChild(rc.cloneNode(true));
        hasContentInCurrentRun = true;
      }
    }

    if (hasContentInCurrentRun) {
      const oldRPr = runNode.getElementsByTagName("w:rPr")[0];
      if (oldRPr) {
        currentRun.insertBefore(oldRPr.cloneNode(true), currentRun.firstChild || null);
      }
      currentP.appendChild(currentRun);
    }
  }

  for (let i = 0; i < p.childNodes.length; i++) {
    const child = p.childNodes[i];
    if (!child) continue;

    if (child.nodeName === "w:r") {
      handleRunNode(child);
    } else {
      currentP.appendChild(child.cloneNode(true));
    }
  }

  return newParagraphs;
}


async function processDocx(inputPath, outputPath) {
  const zip = new JSZip();
  const data = await zip.loadAsync(fs.readFileSync(inputPath));

  const xml = await data.file("word/document.xml").async("string");
  const doc = new DOMParser().parseFromString(xml, "text/xml");
  const body = doc.getElementsByTagName("w:body")[0];

  const paragraphs = Array.from(body.getElementsByTagName("w:p"));
  const newBodyChildren = [];

  let currentBoldText = null;
  let currentBoldRun = null;
  let currentPinyin = null;
  let buffer = [];

  function flushGroup() {
    if (!currentBoldText) return;

    const p = doc.createElement("w:p");

    const runWord = doc.createElement("w:r");
    const rPrWord = currentBoldRun ? cloneRunStyle(currentBoldRun, doc) : doc.createElement("w:rPr");
    rPrWord.appendChild(doc.createElement("w:b"));
    runWord.appendChild(rPrWord);
    runWord.appendChild(doc.createElement("w:t")).appendChild(doc.createTextNode(currentBoldText));
    p.appendChild(runWord);

    if (currentPinyin) {
      const spaceRun = doc.createElement("w:r");
      spaceRun.appendChild(doc.createElement("w:t")).appendChild(doc.createTextNode(" "));
      p.appendChild(spaceRun);

      const runPinyin = doc.createElement("w:r");
      const rPrPin = currentBoldRun ? cloneRunStyle(currentBoldRun, doc) : doc.createElement("w:rPr");
      rPrPin.appendChild(doc.createElement("w:b"));
      runPinyin.appendChild(rPrPin);
      runPinyin.appendChild(doc.createElement("w:t")).appendChild(doc.createTextNode(currentPinyin));
      p.appendChild(runPinyin);
    }

    newBodyChildren.push(p);
    buffer.forEach(node => newBodyChildren.push(node));

    currentBoldText = null;
    currentBoldRun = null;
    currentPinyin = null;
    buffer = [];
  }

  for (const p of paragraphs) {
    const splits = splitParagraphAtLineBreak(p, doc);

    for (const realP of splits) {
      const text = realP.textContent.trim();
      if (!text) continue;

      const boldRun = findBoldRun(realP);

      if (boldRun) {
        flushGroup();
        currentBoldRun = boldRun;
        currentBoldText = text;
      } else if (!currentBoldText) {
        newBodyChildren.push(realP);
      } else {
        if (!currentPinyin && isPinyin(text)) {
          currentPinyin = text;
        } else {
          buffer.push(realP);
        }
      }
    }
  }

  flushGroup();

  if (newBodyChildren.length > 0) {
    while (body.firstChild) body.removeChild(body.firstChild);
    newBodyChildren.forEach(n => body.appendChild(n));
  }

  const updatedXML = new XMLSerializer().serializeToString(doc);
  zip.file("word/document.xml", updatedXML);

  const outBuf = await zip.generateAsync({ type: "nodebuffer" });
  fs.writeFileSync(outputPath, outBuf);
}


// -------------------------------------------------------------------------
// ★★★★★ 批量处理 docs 文件夹下所有 .docx 文件 ★★★★★
// -------------------------------------------------------------------------

async function batchProcess() {
  const inputDir = path.join(process.cwd(), "docs");
  const outputDir = path.join(process.cwd(), "docs2");

  if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir);

  const files = fs.readdirSync(inputDir).filter(f => f.endsWith(".docx"));

  console.log(`共发现 ${files.length} 个 docx 文件，开始处理...\n`);

  for (const file of files) {
    const inputPath = path.join(inputDir, file);
    const outputPath = path.join(outputDir, file);

    console.log(`处理文件：${file}`);
    await processDocx(inputPath, outputPath);
  }

  console.log("\n全部文件处理完成！");
}

batchProcess();
